# This module defines 3d geometrical vectors with the standard
# operations on them. The elements are stored in an
# array.
#
# Written by Konrad Hinsen <khinsen@cea.fr>
# last revision: 2005-9-5
#
# Ported out of Scientific.Geometry by YW Huang <whuang@lineartime.com>
#

import math;
_undocumented = 1

class Vector:

    """Vector in 3D space

    Constructor:

    - Vector(|x|, |y|, |z|)   (from three coordinates)
    - Vector(|coordinates|)   (from any sequence containing three coordinates)

    Vectors support the usual arithmetic operations
    ('v1', 'v2': vectors, 's': scalar):

    -  'v1+v2'           (addition)
    -  'v1-v2'           (subtraction)
    -  'v1*v2'           (scalar product)
    -  's*v1', 'v1*s'    (multiplication with a scalar)
    -  'v1/s'            (division by a scalar)

    The three coordinates can be extracted by indexing.

    Vectors are *immutable*, i.e. their elements cannot be changed.

    Vector elements can be any objects on which the standard
    arithmetic operations plus the functions sqrt and arccos are defined.
    """

    is_vector = 1

    def __init__(self, x=None, y=None, z=None):
        if x is None:
            self.array = [0.0, 0.0, 0.0];
        else:
            self.array = [x, y, z];

    def __repr__(self):
        return 'Vector(%s,%s,%s)' % (`self.array[0]`, `self.array[1]`, `self.array[2]`);

    def __str__(self):
        return `list(self.array)`;

    def __add__(self, other):
        return Vector( self.array[0]+other.array[0], self.array[1]+other.array[1], self.array[2]+other.array[2] );
    __radd__ = __add__;

    def __neg__(self):
        return Vector( -self.array[0], -self.array[1], -self.array[2] );

    def __sub__(self, other):
        return Vector( self.array[0]-other.array[0], self.array[1]-other.array[1], self.array[2]-other.array[2] );

    def __rsub__(self, other):
        return Vector( other.array[0]-self.array[0], other.array[1]-self.array[1], other.array[2]-self.array[2] );

    def __mul__(self, other):

        if isVector(other):
            return self.dot(other)
        return Vector( other*self.array[0], other*self.array[1], other*self.array[2] );

    __rmul__ = __mul__;

    def __div__(self, other):
        if isVector(other):
            raise TypeError("Can't divide by a vector");
        else:
            other = other * 1.0;
            return Vector( self.array[0] / other, self.array[1] / other, self.array[2] / other );

    def __rdiv__(self, other):
        raise TypeError("Can't divide by a vector");

    def __len__(self):
        return 3;

    def __getitem__(self, index):
        return self.array[index];

    def x(self):
        return self.array[0];

    def y(self):
        return self.array[1];

    def z(self):
        return self.array[2];

    def length(self):
        return math.sqrt( self.array[0]**2 + self.array[1]**2 + self.array[2]**2 );

    def normal(self):
        return self/self.length();

    def cross(self, other):
        return Vector( self.array[1]*other.array[2]
                                -self.array[2]*other.array[1],
                      self.array[2]*other.array[0]
                                -self.array[0]*other.array[2],
                      self.array[0]*other.array[1]
                                -self.array[1]*other.array[0]);

    def dot(self,other):
        return self.array[0]*other.array[0] + self.array[1]*other.array[1] + self.array[2]*other.array[2]

def isVector(x):
    return hasattr(x, 'is_vector');
